package com.jiazixuan.stock.security.config;

import com.jiazixuan.stock.security.filter.JwtAuthorizationFilter;
import com.jiazixuan.stock.security.filter.JwtLoginAuthenticationFilter;
import com.jiazixuan.stock.security.handler.StockAccessDeniedHandler;
import com.jiazixuan.stock.security.handler.StockAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity//开启web安全设置生效
//开启SpringSecurity相关注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 定义公共的无需被拦截的资源
     * @return
     */
    private String[] getPubPath(){
        //公共访问资源
        String[] urls = {
                "/**/*.css","/**/*.js","/favicon.ico","/doc.html",
                "/druid/**","/webjars/**","/v2/api-docs","/api/captcha","/api/user",
                "/swagger/**","/swagger-resources/**","/swagger-ui.html","/api/quot/stock/screen/weekkline","/api/quot/stock/screen/dkline"
        };
        return urls;
    }
    /**
     * 构建认证服务，并将对象注入spring IOC容器，用户登录时，会调用该服务进行用户合法信息认证
     * @return
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //登出功能
        http.logout().logoutUrl("/api/logout").invalidateHttpSession(true);
        //开启允许iframe 嵌套。security默认禁用ifram跨域与缓存
        http.headers().frameOptions().disable().cacheControl().disable();
        //session禁用
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.csrf().disable();//禁用跨站请求伪造
        http.authorizeRequests()//对资源进行认证处理
                .antMatchers(getPubPath()).permitAll()//公共资源都允许访问
                .anyRequest().authenticated();  //除了上述资源外，其它资源，只有 认证通过后，才能有权访问

        //将自定义的过滤器加入security过滤器链，且在默认的认证过滤器之前执行
        http.addFilterBefore(jwtLoginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        //配置授权过滤器，它是资源安全的屏障，优先级最高，所以要再自定义的认证过滤器之前
        http.addFilterBefore(jwtAuthorizationFilter(),JwtLoginAuthenticationFilter.class);
//        //配置访问拒绝处
        http.exceptionHandling().accessDeniedHandler(new StockAccessDeniedHandler())
                .authenticationEntryPoint(new StockAuthenticationEntryPoint());
    }

    /**
     * 自定义认证过滤器
     *      隐含：如果认证成功。则在安全上下文中维护认证相关信息
     *           如果上下文中存在，则默认的UsernamePasswordAuthenticationFilter没必要再认证
     *           所以执行顺序自定义的在前，一旦成功则默认的就不用再执行
     * @return
     * @throws Exception
     */
    @Bean
    public JwtLoginAuthenticationFilter jwtLoginAuthenticationFilter() throws Exception {
        //构造认证过滤器对象，并设置认证路径
        JwtLoginAuthenticationFilter authenticationFilter = new JwtLoginAuthenticationFilter("/api/login");
        //设置认证过滤器bean
        authenticationFilter.setAuthenticationManager(authenticationManagerBean());
        //注入redis
        authenticationFilter.setRedisTemplate(redisTemplate);
        return authenticationFilter;
    }


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置授权过滤器
     * @return
     */
    @Bean
    public JwtAuthorizationFilter jwtAuthorizationFilter(){
        return new JwtAuthorizationFilter();
    }

}